package repository

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"
	// "github.com/spf13/viper"
)

/*
 * ------------------------------------
 *       构建POST GET PUT三种方法
 * ------------------------------------
 */
type FuncVar struct {
	IP       string
	Port     string
	API      string `json:"api,omitempty"`
	User     string `json:"user,omitempty"`
	Password string `json:"password,omitempty"`
	Token    string `json:"token,omitempty"`
}

func postData(url FuncVar, d []byte) *http.Response {

	targetUrl := "http://" + url.IP + ":" + url.Port + url.API // 目标url
	log.Printf("POST TARGET: %s \n", targetUrl)

	req, err := http.NewRequest("POST", targetUrl, bytes.NewBuffer(d))
	if err != nil {
		log.Printf("ERROR: Create HTTP POST request failed %s \n", targetUrl)
		panic(err)
	}

	defer req.Body.Close()                                           // 必须设定该参数,POST参数才能正常提交
	req.Header.Set("Content-Type", "application/json;charset=UTF-8") // 请求头，意思是以json串提交数据

	if url.Token != "" { // basic auth 认证
		req.Header.Set("Authorization", "Bearer "+url.Token)
	} else if (url.User != "") && (url.Password != "") {
		req.SetBasicAuth(url.User, url.Password)
	}

	response, err := http.DefaultClient.Do(req) // 响应
	if err != nil {
		log.Printf("ERROR: Send HTTP POST request failed %s \n", targetUrl)
		panic(err)
	}
	return response
}

func getData(url FuncVar) *http.Response {
	targetUrl := "http://" + url.IP + ":" + url.Port + url.API // 目标url
	log.Printf("GET TARGET: %s \n", targetUrl)

	req, err := http.NewRequest("GET", targetUrl, nil)
	if err != nil {
		log.Printf("ERROR: Create HTTP POST request failed %s \n", targetUrl)
		panic(err)
	}

	if url.Token != "" {
		req.Header.Set("Authorization", "Bearer "+url.Token)
	} else if (url.User != "") && (url.Password != "") {
		req.SetBasicAuth(url.User, url.Password)
	}

	response, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Printf("ERROR: Send HTTP POST request failed %s \n", targetUrl)
		panic(err)
	}
	return response
}

func putData(url FuncVar, d []byte) *http.Response {

	targetUrl := "http://" + url.IP + ":" + url.Port + url.API // 带header的post方法
	log.Printf("PUT TARGET: %s \n", targetUrl)

	data := strings.NewReader(string(d))

	var req *http.Request

	if data == nil {
		req, _ = http.NewRequest("PUT", targetUrl, nil)
	} else {
		req, _ = http.NewRequest("PUT", targetUrl, data)
	}

	req.Header.Set("Content-Type", "application/json;charset=UTF-8")

	if url.Token != "" { // basic auth 认证
		req.Header.Set("Authorization", "Bearer "+url.Token)
	} else if (url.User != "") && (url.Password != "") {
		req.SetBasicAuth(url.User, url.Password)
	}

	response, _ := http.DefaultClient.Do(req) // 读取post返回的数据
	return response
}

/*
 * ------------------------------------
 *       构建AWX调用api方法
 * ------------------------------------
 */
type AwxPara struct {
	TemplateName   string
	AwxInventory   AwxInventory
	InstanceGroups InstanceGroups
	AwxGroups      AwxGroups
	AwxGroupsHosts []AwxGroupsHosts
	AwxVar         AwxVar
	LaunchPara     LaunchPara
}

type AwxInventory struct {
	Name         string `json:"name"`
	Description  string `json:"description"`
	Organization int    `json:"organization"`
	Kind         string `json:"kind,omitempty"`
}

type InstanceGroups struct {
	ID int `json:"id"`
}

type AwxGroups struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	Variables   string `json:"variables,omitempty"`
}

type AwxGroupsHosts struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	Enabled     bool   `json:"enabled,omitempty"`
	InstanceId  string `json:"instance_id,omitempty"`
	Variables   string `json:"variables,omitempty"`
}

type AwxVar struct {
	Variables string `json:"variables"`
}

type LaunchPara struct {
	AskLimitOnLaunch     bool   `json:"ask_limit_on_launch,omitempty"`
	AskScmBranchOnLaunch bool   `json:"ask_scm_branch_on_launch,omitempty"`
	ExtraVars            string `json:"extra_vars,omitempty"`
	Inventory            int    `json:"inventory,omitempty"`
	Limit                string `json:"limit,omitempty"`
	ScmBranch            string `json:"scm_branch,omitempty"`
	Credentials          []int  `json:"credentials,omitempty"`
}

// 网络请求转ID
func reponseToID(r *http.Response) int {
	body, _ := io.ReadAll(r.Body)
	// 万能stuct体
	var data map[string]interface{}
	var id int
	// 解析http返回的json体
	if err := json.Unmarshal(body, &data); err == nil {
		for idx, value := range data {
			if idx == "id" {
				id = int(value.(float64))
				log.Printf("AWX id:%v", id)
			}
		}
	}
	return id
}

// ---------------------------api----------------------------
type Awx struct {
	UrlData FuncVar
	AwxPara AwxPara
}

// 1. 获取job template id
func (c Awx) GetJobTemplates(name string) int {

	c.UrlData.API = "/api/v2/job_templates/?name=" + name // 初始化url结构体参数，不包括具体的API
	response := getData(c.UrlData)                        // 获取template信息
	if response.StatusCode != 200 {                       // 错误处理
		log.Printf("ERROR: Failed to get job_templates of %s", name)
		panic("Failed to get job_templates name")
	} else {
		log.Printf("INFO: get job_templates of %s success", name)
	}
	body, _ := io.ReadAll(response.Body)

	var data map[string]interface{} // 万能stuct体
	var id int

	if err := json.Unmarshal(body, &data); err == nil { // 解析http返回的json体
		for idx, value := range data {
			if idx == "results" {
				dataMidd := value.([]interface{})
				dataResults := dataMidd[0].(map[string]interface{})
				for resultsIndex, resultsValue := range dataResults {
					if resultsIndex == "id" {
						id = int(resultsValue.(float64))
						break
					}
				}
				log.Printf("INFO: %v template id: %v", name, id)
			}
		}
	}
	return id
}

// 2. 创建Inventory
func (c Awx) CreatInventory() int {

	c.UrlData.API = "/api/v2/inventories/"           // 初始化url结构体参数，不包括具体的API
	bytes, _ := json.Marshal(c.AwxPara.AwxInventory) // struct数据转换成json格式的字节格式
	response := postData(c.UrlData, bytes)           // 调用post函数

	if response.StatusCode == 201 {
		log.Printf("INFO: Create inventory success")
	} else if response.StatusCode == 400 {
		body, _ := io.ReadAll(response.Body)
		log.Printf("ERROR: Failed to create inventory, error is %v", string(body))
		panic("Failed to create inventory")
	} else {
		log.Printf("WARNING: Not have permission to create inventory")
	}

	return reponseToID(response)
}

// 3. 给inventory 添加 instance_groups
func (c Awx) CreatInstanceGroupsForInventory(i int) {

	c.UrlData.API = "/api/v2/inventories/" + strconv.Itoa(i) + "/instance_groups/" // 初始化url结构体参数，不包括具体的API
	bytes, _ := json.Marshal(c.AwxPara.InstanceGroups)
	response := postData(c.UrlData, bytes) // 调用post函数

	if response.StatusCode != 204 && response.StatusCode != 201 {
		log.Printf("ERROR: Failed to create instance groups for inventory")
		panic("Failed to create instance groups for inventory")
	} else {
		log.Printf("INFO: Create instance groups for inventory success")
	}
}

// 4. 给inventory 添加 groups
func (c Awx) CreatGroupsForInventory(i int) int {

	c.UrlData.API = "/api/v2/inventories/" + strconv.Itoa(i) + "/groups/" // 初始化url结构体参数，不包括具体的API
	bytes, _ := json.Marshal(c.AwxPara.AwxGroups)                         // struct数据转换成json格式的字节格式
	response := postData(c.UrlData, bytes)                                // 调用post函数

	if response.StatusCode == 201 {
		log.Printf("INFO: Create groups for inventory success")
	} else if response.StatusCode == 400 {
		body, _ := io.ReadAll(response.Body)
		log.Printf("ERROR: Failed to create groups for inventory, error is %v", string(body))
		panic("Failed to create groups for inventory")
	} else {
		log.Printf("WARNING: Do not have permission to create groups for inventory")
	}
	return reponseToID(response)
}

// 5. 给groups添加hosts
func (c Awx) CreatHostsFoGroupsT(i int) {

	c.UrlData.API = "/api/v2/groups/" + strconv.Itoa(i) + "/hosts/" // 初始化url结构体参数，不包括具体的API

	for _, host := range c.AwxPara.AwxGroupsHosts { // 逐个添加host
		bytes, _ := json.Marshal(&host)
		response := postData(c.UrlData, bytes)
		if response.StatusCode != 201 {
			log.Printf("ERROR: Failed to add host, Do not have permission to add host")
			panic("Failed to add host")
		} else {
			log.Printf("INFO: Add host success")
		}
	}
}

// 5. 给groups添加hosts
func (c Awx) CreatHostsFoGroups(i int) {

	c.UrlData.API = "/api/v2/groups/" + strconv.Itoa(i) + "/hosts/" // 初始化url结构体参数，不包括具体的API
	type Group struct {
		ID    int              `json:"-"`
		Name  string           `json:"-"`
		Hosts []AwxGroupsHosts `json:"hosts"`
	}
	requestBody := Group{
		Hosts: c.AwxPara.AwxGroupsHosts,
	}
	bytes, _ := json.Marshal(&requestBody)
	response := postData(c.UrlData, bytes)
	if response.StatusCode != 201 {
		log.Printf("Failed to add host, Do not have permission to add host")
		panic("Failed to add host")
	} else {
		log.Printf("Add host success")
	}
}

// 6. 更新hosts
func (c Awx) UpdateVariableForHosts(i int) {

	c.UrlData.API = "/api/v2/hosts/" + strconv.Itoa(i) + "/" // 初始化url结构体参数，不包括具体的API
	bytes, _ := json.Marshal(c.AwxPara.AwxVar)               // struct数据转换成json格式的字节格式
	response := putData(c.UrlData, bytes)                    // 调用post函数
	body, _ := io.ReadAll(response.Body)
	fmt.Println(string(body))
}

// 7. 运行job
func (c Awx) LaunchJobTemplate(i int) int {

	c.UrlData.API = "/api/v2/job_templates/" + strconv.Itoa(i) + "/launch/" // 初始化url结构体参数，不包括具体的API
	bytes, _ := json.Marshal(c.AwxPara.LaunchPara)
	response := postData(c.UrlData, bytes) // 调用post函数
	if response.StatusCode == 201 {
		log.Printf("Launch job success")
	} else if response.StatusCode == 400 {
		body, _ := io.ReadAll(response.Body)
		log.Printf("Failed to launch job, error is %v", string(body))
		panic("Failed to launch job")
	} else {
		log.Printf("Do not have permission to launch job")
	}
	return reponseToID(response)
}

// 8. 获取jobs 状态
func (c Awx) GetJobs(i int) string {

	c.UrlData.API = "/api/v2/jobs/" + strconv.Itoa(i) + "/job_events/" // 初始化url结构体参数，不包括具体的API
	var state string
	var data map[string]interface{} // 万能stuct体
	for {
		time.Sleep(1 * time.Second)
		response := getData(c.UrlData)
		body, _ := io.ReadAll(response.Body)
		var stdOut []string
		// 解析http返回的json体
		if err := json.Unmarshal(body, &data); err == nil {
			if data["detail"] == "Not found." {
				fmt.Println("Canot find Jobs")
				break
			}
			if data["count"] == nil {
				fmt.Println("count is nil")
			} else if data["count"].(float64) != 0 {

				dataResults := data["results"].([]interface{})

				for _, value := range dataResults {
					dataResultsSlice := value.(map[string]interface{})
					stdOut = append(stdOut, dataResultsSlice["stdout"].(string))

				}
				fmt.Println("stdOut", stdOut)
				dataResultsMap := dataResults[0].(map[string]interface{})
				dataResultsSummary := dataResultsMap["summary_fields"].(map[string]interface{})
				dataResultsSummaryJob := dataResultsSummary["job"].(map[string]interface{})

				if dataResultsSummaryJob["status"] == "failed" || dataResultsSummaryJob["status"] == "successful" {
					state = dataResultsSummaryJob["status"].(string)
					fmt.Println(state)
					break
				}
			}
		}
		time.Sleep(9 * time.Second)
	}
	return state
}

// 9. 获取jobs event状态
func (c Awx) GetJobsEvent(i int) (string, string) {

	c.UrlData.API = "/api/v2/jobs/" + strconv.Itoa(i) + "/job_events/" // 初始化url结构体参数，不包括具体的API
	var data map[string]interface{}                                    // 万能stuct体
	var state string
	response := getData(c.UrlData)
	body, _ := io.ReadAll(response.Body)
	var stdOut string

	if err := json.Unmarshal(body, &data); err == nil { // 解析http返回的json体
		if data["detail"] == "Not found." {
			fmt.Println("Canot find Jobs")
		}
		if data["count"] == nil {
			fmt.Println("count is nil")
		} else if data["count"].(float64) != 0 {

			dataResults := data["results"].([]interface{})

			for _, value := range dataResults {
				dataResultsSlice := value.(map[string]interface{})
				fmt.Println(dataResultsSlice["stdout"].(string))
				stdOut = stdOut + dataResultsSlice["stdout"].(string)
			}
			dataResultsMap := dataResults[0].(map[string]interface{})
			dataResultsSummary := dataResultsMap["summary_fields"].(map[string]interface{})
			dataResultsSummaryJob := dataResultsSummary["job"].(map[string]interface{})

			if dataResultsSummaryJob["status"] == "failed" || dataResultsSummaryJob["status"] == "successful" {
				state = dataResultsSummaryJob["status"].(string)
				fmt.Println(state)
			}
		}
	}
	return state, stdOut
}
